// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of the CIdResolver class.
//   $Id$

#include "stdafx.h"
#include "IdResolver.h"
#include "formcontrol.h"
#include "xformsxformselement.h"
#include "FP_ReturnCodes.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CIdResolver::CIdResolver(CImpl * pOwner, CXFormsXFormsElement * pOwnerForm)
	:m_pOwner(pOwner)
	,m_pOwnerForm(pOwnerForm)
	,m_pCurrentIteratorMap(0)
{

}

CIdResolver::~CIdResolver()
{
	m_pOwner = 0;
	m_pOwnerForm =  0;
}

HRESULT CIdResolver::AddItem(CImpl *pImpl)
{	
	HRESULT hr = S_FALSE;
	if(!!pImpl->m_sId)
	{
		CMapBSTRToCImplPtr::iterator i = m_NodeMap.find(pImpl->m_sId);
		if(i != m_NodeMap.end())
		{
			//Attempting to insert a duplicate id
			//	is an error.
			hr = E_FAIL;
		}
		else
		{
			//insert the object into the map of ids.
			m_NodeMap.insert(CPairBSTRAndCImplPtr(pImpl->m_sId,pImpl));
			if(m_pOwner)
			{
				CIdResolver * pParentResolver = 0;
				if(m_pOwner->m_pParentImpl)
				{
					//Will need to add this as scope to the parent.
					pParentResolver = m_pOwner->m_pParentImpl->GetIdResolver();
				}
				else
				{
					//Will need to add this as scope to the parent.
					pParentResolver = &m_pOwnerForm->m_IdResolver;
				}
				if(pParentResolver != this)
					pParentResolver->AddScopedId(pImpl->m_sId.m_str,m_pOwner);
			}
			hr = S_OK;
		}
	}
	return hr;
}

HRESULT CIdResolver::AddScopedId(BSTR bsId, CImpl *pScopeContainer)
{
	HRESULT hr = S_FALSE;
	if(*bsId != '\0')
	{
		CMapBSTRToCImplPtr::iterator i = m_ScopeMap.find(bsId);
		if(i != m_ScopeMap.end())
		{
			//Attempting to insert a duplicate id
			//	is an error.
			hr = E_FAIL;
		}
		else
		{
			//insert the container into the scope map
			m_ScopeMap.insert(CPairBSTRAndCImplPtr(bsId,pScopeContainer));
			hr = S_OK;

			//Insert into parental scope map.
			if(m_pOwner)
			{
				CIdResolver * pParentResolver = 0;
				if(m_pOwner->m_pParentImpl)
				{
					//Will need to add this as scope to the parent.
					pParentResolver = m_pOwner->m_pParentImpl->GetIdResolver();
				}
				else
				{
					//Will need to add this as scope to the parent.
					pParentResolver = &m_pOwnerForm->m_IdResolver;
				}
				if(pParentResolver != this)
					pParentResolver->AddScopedId(bsId,m_pOwner);
			}
		}
		
	}
	return hr;
}

bool CIdResolver::IterateIds(BSTR *pbsRetVal)
{
	if(m_pCurrentIteratorMap && (m_IteratorPos != m_pCurrentIteratorMap->end()))
	{
		//get the string out of the map and put it into the
		//	out value.

		*pbsRetVal = m_IteratorPos->first.Copy();
		
		//move on		
		++m_IteratorPos;
		if(m_IteratorPos != m_pCurrentIteratorMap->end() && (m_pCurrentIteratorMap == &m_NodeMap))
		{
			m_pCurrentIteratorMap = &m_ScopeMap;
			m_IteratorPos = m_pCurrentIteratorMap->begin();
		}
		return true;
	}
	else
	{
		return false;
	}
}

void CIdResolver::ResetIterator()
{
	//set the iteration map to point to the node map
	m_pCurrentIteratorMap = &m_NodeMap;
	m_IteratorPos = m_pCurrentIteratorMap->begin();
}


HRESULT CIdResolver::getItem(const BSTR bsId, CImpl*& pItem) const
{
	HRESULT hr = S_FALSE;

	if (bsId)
	{
		CMapBSTRToCImplPtr::const_iterator iNodeIterator = m_NodeMap.find(bsId);

		if(iNodeIterator != m_NodeMap.end())
		{
			pItem = iNodeIterator->second;
			//id corresponds to an object dirctly within our scope.
			hr = S_OK;
		}
		else 
		{
			CMapBSTRToCImplPtr::const_iterator iScopeIterator = m_ScopeMap.find(bsId);
			if(iScopeIterator != m_ScopeMap.end())
			{
				CImpl * pScope = iScopeIterator->second;
				CIdResolver * pResolver = pScope->GetIdResolver();
				if(pResolver)
				{
					if(pResolver != this)
						//id is in a nested resolver of this object
						hr = pResolver->getItem(bsId,pItem);
					else
					{
						hr = E_FAIL;
						pItem = 0;
						ATLTRACE("pResolver is this, infinite loop ");
					}
				}
			}
			else
			{
				// id cannot be found here or lower, it must be higher.
				if(m_pOwner)
				{
					if(m_pOwner->m_pParentImpl)
					{
						//try to get the owner's parent's resolver
						CIdResolver * pResolver =  m_pOwner->m_pParentImpl->GetIdResolver();
						if(pResolver)
						{
							if(pResolver!=this)
								//id is in a nested resolver of this object
								hr = pResolver->getItem(bsId,pItem);
							else
							{
								hr = E_FAIL;
								pItem = 0;
								ATLTRACE("pResolver is this, infinite loop ");
							}
						}

					}
					else
					{
						//we are at the first level in from the top,
						//	get the top level id resolver.
						hr = m_pOwnerForm->m_IdResolver.getItem(bsId,pItem);
					}
				}
				else
				{
					//if there is no owner,we are at the top level,
					//	if we cannot find an id here, it is an error.
					hr = FP_E_BADID;
					::SetupErrorInfo(hr,bsId);
					pItem = NULL;
				}
			}
		}
	}//if ( we were passed an ID )
	else
	{
		hr = E_FAIL;
		pItem = NULL;
	}
	return hr;
}

void CIdResolver::Remove(const BSTR bsId)
{
	// Check with the owner whether it is ok to remove the id. The 
	// only situation where it isn't is when the xf:group is a repeat
	// iteration.
	if(!m_pOwner || (m_pOwner && m_pOwner->CanRemoveIdFromResolver()))
		m_NodeMap.erase(bsId);
	//TODO: propagate up?
	//	There is no real need to propagate up, 
	// The only time it will matter is if there
	//	are lots of ids and deep repeats to go
	//	even then, I'm sure it wont matter.
}
